#include "cpu/cpu.h"
#include "os_cfg.h"
#include "comm/cpu_instr.h"
#include "cpu/irq.h"
#include "ipc/mutex.h"
#include "core/syscall.h"

static segment_desc_t gdt_table[GDT_TABLE_SIZE];
static mutex_t mutex;

//gdt表项设置
void segment_desc_set(int selector,uint32_t base,uint32_t limit,uint16_t attr){
    segment_desc_t * desc = gdt_table + selector / sizeof(segment_desc_t);

    // 如果界限比较长，将长度单位换成4KB
	if (limit > 0xfffff) {
		attr |= SEG_G;
		limit /= 0x1000;
	}

    desc-> limit15_0 = limit & 0xFFFF;
    desc-> base15_0 = base & 0xFFFF;
    desc-> base23_16 = (base>>16) & 0xFF;
    desc-> attr = attr | (((limit>>16)&0xF)<<8);
    desc-> base31_24 = (base>>24)&0xFF;
}

void gate_desc_set(gate_desc_t* desc,uint16_t selector,uint32_t offset,uint16_t attr){
    desc->offset15_0 = offset&0xFFFF;
    desc->selector = selector;
    desc->attr = attr;
    desc->offset31_16 = offset>>16;
}

int gdt_alloc_desc(){
    mutex_lock(&mutex);
    for(int i=1;i<GDT_TABLE_SIZE;i++){
        segment_desc_t *desc = gdt_table+i;
        if(desc->attr == 0){
            mutex_unlock(&mutex);   
            return i * sizeof(segment_desc_t);
        }
    }
    mutex_unlock(&mutex);
    return -1;
}


void gdt_free_seg(int tss_seg){
    mutex_lock(&mutex);
    gdt_table[tss_seg/sizeof(segment_desc_t)].attr = 0;
    mutex_unlock(&mutex);
}

void init_gdt(){
    for (int i = 0; i < GDT_TABLE_SIZE; i++)
    {
        segment_desc_set(i<<3,0,0,0);
    }
    
    //设置内核的代码段和数据段
    segment_desc_set(KERNEL_SELECTOR_CS,0x0,0xFFFFFFFF,
        SEG_D|SEG_DPL0|SEG_S_NORMAL|SEG_TYPE_CODE|SEG_TYPE_RW|SEG_P_PRESENT
    );
    segment_desc_set(KERNEL_SELECTOR_DS,0x0,0xFFFFFFFF,
        SEG_D|SEG_DPL0|SEG_S_NORMAL|SEG_TYPE_DATA|SEG_TYPE_RW|SEG_P_PRESENT
    );

    //系统调用门描述符
    gate_desc_set((gate_desc_t*)(gdt_table+(SELECTOR_SYSCALL>>3)),
        KERNEL_SELECTOR_CS,(uint32_t)exception_handler_syscall,
        GATE_P_PRESENT|GATE_DPL3|GATE_TYPE_SYSCALL|SYSCALL_PARAM_COUNT 
    );

    lgdt((uint32_t)gdt_table,sizeof(gdt_table));    //重新加载gdt表
}

void cpu_init(){
    mutex_init(&mutex);
    init_gdt();
}

void switch_to_tss(int tss_seg){
    far_jump(tss_seg,0);
}
